﻿using System;

using System.Collections.Generic;
using System.Text;
using GraphAPI.Model;
using System.IO;
using System.Xml;
using System.Drawing;

namespace GraphAPI
{
	public class GraphXmlSerializer
	{
		public static void Serialize(Graph graph, Stream stream)
		{
			XmlTextWriter writer = new XmlTextWriter(stream, Encoding.UTF8);
			writer.Formatting = Formatting.Indented;
			writer.WriteStartDocument();
			writer.WriteStartElement("graph");
			foreach (Entity ent in graph.Entitys)
			{
				SerializeEntity(ent, writer);
			}

			foreach (Relation rel in graph.Relations)
			{
				SerializeRelation(graph, rel, writer);
			}

			writer.WriteEndElement();
			writer.Close();
		}

		private static void SerializeEntity(Entity ent, XmlTextWriter writer)
		{
			writer.WriteStartElement("entity");
			if (ent.Name.Length > 0)
			{
				writer.WriteStartElement("name");
				writer.WriteString(ent.Name);
				writer.WriteEndElement();
			}
			if (ent.Commentary.Length > 0)
			{
				writer.WriteStartElement("comments");
				writer.WriteString(ent.Commentary);
				writer.WriteEndElement();
			}
			writer.WriteStartElement("position");
			writer.WriteString(ent.Position.X.ToString() + ";" + ent.Position.Y.ToString());
			writer.WriteEndElement();
			writer.WriteStartElement("mark");
			if (ent.ColorMark.Equals(Color.Empty))
				writer.WriteString("default");
			else
				if (ent.ColorMark.IsKnownColor)
					writer.WriteString(ent.ColorMark.ToKnownColor().ToString());
				else
					writer.WriteString(ent.ColorMark.ToArgb().ToString());
			writer.WriteEndElement();			writer.WriteStartElement("weight");
			writer.WriteString(ent.Weight.ToString());
			writer.WriteEndElement();
			writer.WriteStartElement("radius");
			writer.WriteString(ent.Radius.ToString());
			writer.WriteEndElement();
			writer.WriteStartElement("align");
			writer.WriteString(ent.TextAlign.ToString());
			writer.WriteEndElement();
			writer.WriteEndElement();
		}

		private static void SerializeRelation(Graph graph, Relation rel, XmlTextWriter writer)
		{
			writer.WriteStartElement("relation");
			if (rel.Name.Length > 0)
			{
				writer.WriteStartElement("name");
				writer.WriteString(rel.Name);
				writer.WriteEndElement();
			}
			if (rel.Commentary.Length > 0)
			{
				writer.WriteStartElement("comments");
				writer.WriteString(rel.Commentary);
				writer.WriteEndElement();
			}
			writer.WriteStartElement("from");
			writer.WriteString(graph.GetEntityId(rel.StartEntity).ToString());
			writer.WriteEndElement();
			writer.WriteStartElement("to");
			writer.WriteString(graph.GetEntityId(rel.EndEntity).ToString());
			writer.WriteEndElement();
			writer.WriteStartElement("oriented");
			writer.WriteString(rel.Oriented.ToString());
			writer.WriteEndElement();
			writer.WriteStartElement("mark");
			if (rel.ColorMark.Equals(Color.Empty))
				writer.WriteString("default");
			else
				if (rel.ColorMark.IsKnownColor)
					writer.WriteString(rel.ColorMark.ToKnownColor().ToString());
				else
					writer.WriteString(rel.ColorMark.ToArgb().ToString());
			writer.WriteEndElement();
			writer.WriteStartElement("weight");
			writer.WriteString(rel.Weight.ToString());
			writer.WriteEndElement();
			writer.WriteEndElement();
		}

		public static Graph DeserializeGraph(Stream stream)
		{
			Graph graph = new Graph();

			XmlTextReader reader = new XmlTextReader(stream);
			reader.WhitespaceHandling = WhitespaceHandling.None;

			while (reader.Read())
			{
				XmlNodeType type = reader.MoveToContent();
				if (type == XmlNodeType.Element && reader.Name == "entity")
				{
					graph.AddEntity(DeserializeEntity(reader));
				}

				if (type == XmlNodeType.Element && reader.Name == "relation")
				{
					graph.AddRelation(DeserializeRelation(graph, reader));
				}
			}

			reader.Close();

			return graph;
		}

		private static Entity DeserializeEntity(XmlTextReader reader)
		{
			Entity ent = new Entity(new Point());

			while (reader.Read())
			{
				XmlNodeType type = reader.MoveToContent();

				if (type == XmlNodeType.EndElement && reader.Name == "entity")
				{
					break;
				}

				if (type == XmlNodeType.Element)
				{
					switch (reader.Name)
					{
						case "name":
							reader.Read();
							ent.Name = reader.ReadContentAsString();
							break;

						case "comments":
							reader.Read();
							ent.Commentary = reader.ReadContentAsString();
							break;

						case "position":
							reader.Read();
							string pos = reader.ReadContentAsString();
							string[] posParts = pos.Split(';');
							int x = int.Parse(posParts[0]);
							int y = int.Parse(posParts[1]);
							ent.Position = new Point(x, y);
							break;

						case "radius":
							reader.Read();
							ent.Radius = int.Parse(reader.ReadContentAsString());
							break;

						case "weight":
							reader.Read();
							ent.Weight = double.Parse(reader.ReadContentAsString());
							break;

						case "mark":
							reader.Read();
							string color = reader.ReadContentAsString();
							if (color == "default")
								ent.ColorMark = Color.Empty;
							else
							{
								try
								{
									ent.ColorMark = Color.FromKnownColor((KnownColor)Enum.Parse(typeof(KnownColor), color));
								}
								catch
								{
									ent.ColorMark = Color.FromArgb(int.Parse(color));
								}
							}
							break;

						case "align":
							reader.Read();
							ent.TextAlign = (TextAlign)Enum.Parse(typeof(TextAlign), reader.ReadContentAsString());
							break;
					}
				}
			}

			return ent;
		}

		private static Relation DeserializeRelation(Graph graph, XmlTextReader reader)
		{
			Entity start = null;
			Entity end = null;
			string name = "";
			string comments = "";
			double weight = 0;
			Color mark = Color.Empty;
			bool oriented = false;

			while (reader.Read())
			{
				XmlNodeType type = reader.MoveToContent();

				if (type == XmlNodeType.EndElement && reader.Name == "relation")
				{
					break;
				}

				if (type == XmlNodeType.Element)
				{
					switch (reader.Name)
					{
						case "name":
							reader.Read();
							name = reader.ReadContentAsString();
							break;

						case "comments":
							reader.Read();
							comments = reader.ReadContentAsString();
							break;

						case "weight":
							reader.Read();
							weight = double.Parse(reader.ReadContentAsString());
							break;

						case "from":
							reader.Read();
							start = graph.Entitys[int.Parse(reader.ReadContentAsString())];
							break;

						case "to":
							reader.Read();
							end = graph.Entitys[int.Parse(reader.ReadContentAsString())];
							break;

						case "mark":
							reader.Read();
							string color = reader.ReadContentAsString();
							if (color == "default")
								mark = Color.Empty;
							else
							{
								try
								{
									mark = Color.FromKnownColor((KnownColor)Enum.Parse(typeof(KnownColor), color));
								}
								catch
								{
									mark = Color.FromArgb(int.Parse(color));
								}
							}
							break;

						case "oriented":
							reader.Read();
							oriented = reader.ReadContentAsString() == true.ToString();
							break;
					}
				}
			}

			Relation rel = new Relation(start, end, oriented, name);
			rel.ColorMark = mark;
			rel.Weight = weight;
			rel.Commentary = comments;
			return rel;
		}
	}
}
